﻿package net.wangit.jdbc.connection;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import net.wangit.framework.config.BeanFactory;
import net.wangit.framework.exception.ConfigurationException;
import net.wangit.jdbc.JDBCException;
import net.wangit.jdbc.transaction.JDBCTransaction;
import net.wangit.jdbc.transaction.JTATransaction;
import net.wangit.jdbc.transaction.TransactionException;
import net.wangit.jdbc.transaction.TransactionImplementor;
import net.wangit.util.Checker;
import net.wangit.util.NamingUtil;
import net.wangit.util.XMLUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;


public class DatasourceConnectionProvider implements ConnectionProvider {

	private TransactionImplementor transaction;
	private DataSource ds;
	private String userName;
	private String password;
	private static final Log log = LogFactory.getLog(DatasourceConnectionProvider.class);

	public DatasourceConnectionProvider() {
	}

	public void configure(String path, Element connectionNode)
			throws ConnectionException, JDBCException {
		Properties jndiProps = getJndiProperties(connectionNode);
		transaction = getTransaction(path, connectionNode
				.element("transaction"), jndiProps);
		String jndiName = XMLUtil.getTextString(connectionNode, "jndi-name",
				null);
		userName = XMLUtil.getTextString(connectionNode, "username", null);
		password = XMLUtil.getTextString(connectionNode, "password", null);
		try {
			ds = (DataSource) NamingUtil.getInitialContext(jndiProps).lookup(
					jndiName);
		} catch (Exception e) {
			throw new ConnectionException(
					(new StringBuilder("could not find datasource: ")).append(
							jndiName).toString(), e);
		}
		if (ds == null)
			throw new ConnectionException((new StringBuilder(
					"could not find datasource: ")).append(jndiName).toString());
		if (log.isDebugEnabled())
			log.debug((new StringBuilder("using datasource: "))
					.append(jndiName).toString());
	}

	public TransactionImplementor getTransaction() {
		return transaction;
	}

	public Connection getConnection() throws SQLException {
		if (!Checker.isEmpty(userName) && !Checker.isEmpty(password))
			return ds.getConnection(userName, password);
		else
			return ds.getConnection();
	}

	public void closeConnection(Connection conn) throws SQLException {
		conn.close();
	}

	private static Properties getJndiProperties(Element connectionNode) {
		Properties result = new Properties();
		String jndiClass = XMLUtil.getTextString(connectionNode, "jndi-class",
				null);
		String jndiURL = XMLUtil
				.getTextString(connectionNode, "jndi-url", null);
		if (!Checker.isEmpty(jndiClass))
			result.put("java.naming.factory.initial", jndiClass);
		if (!Checker.isEmpty(jndiURL))
			result.put("java.naming.provider.url", jndiURL);
		List<?> parameters = connectionNode.elements("parameter");
		if (parameters != null) {
			for (int i = 0; i < parameters.size(); i++) {
				Element parameter = (Element) parameters.get(i);
				result.setProperty(parameter.attributeValue("name"), parameter
						.attributeValue("value"));
			}

		}
		return result;
	}

	private TransactionImplementor getTransaction(String path,
			Element transactionNode, Properties jndiProps)
			throws TransactionException {
		if (transactionNode == null)
			return new JDBCTransaction();
		String transactionName = XMLUtil.getAttributeString(transactionNode,
				"name", "jdbc");
		TransactionImplementor transaction;
		try {
			transaction = BeanFactory.getTransaction(transactionName);
		} catch (Exception e) {
			throw new ConfigurationException(path, "transaction");
		}
		log.info((new StringBuilder("use transaction : ")).append(
				transactionName).toString());
		if (transaction instanceof JTATransaction) {
			String jtaUserTransaction = XMLUtil.getAttributeString(
					transactionNode, "jta-user-transaction", null);
			((JTATransaction) transaction).configure(jndiProps,
					jtaUserTransaction);
		}
		return transaction;
	}

}
